#include "game.h"
#include "actor.h"
#include "tile.h"
#include "item.h"
#include "level.h"
#include "queue.h"
#include "extras.h"
#include <fstream>
#include "buffer.h"
using std::ofstream;
using std::ifstream;
using std::endl;

extern Actor Player,LoadedActor[NUM_ACTORS];
extern AttackInfo LoadedAttack[NUM_ATTACKS];
extern Tile LoadedTile[NUM_TILES];
extern Item LoadedItem[NUM_ITEMS];
extern Level Map;
extern Queue Q;
extern Buffer Buff;

GameInfo::GameInfo() : turn_count(0),currentlevel(TOWN),recall_distance(0),old_level(TOWN){
	//any options that default to true go here:
//	options[ALLOW_ARROWS]=true;
}
void GameInfo::InitTiles(){
	LoadedTile[0].Define(0,"floor",'.',B_WHITE,true,false,false,-1);
	LoadedTile[1].Define(1,"wall",'#',B_WHITE,false,true,false,-1);
	LoadedTile[2].Define(2,"open door",'-',B_BLACK,true,false,true,3);
	LoadedTile[3].Define(3,"closed door",'+',B_BLACK,true,true,true,2);
	LoadedTile[4].Define(4,"down stairway",'>',B_WHITE,true,false,false,-1);
	LoadedTile[5].Define(5,"tree",'#',GREEN,false,true,false,-1);
	LoadedTile[6].Define(6,"ground",'.',YELLOW,true,false,false,-1);
	LoadedTile[7].Define(7,"up stairway",'<',B_WHITE,true,false,false,-1);
}
void GameInfo::InitAttacks(){
	LoadedAttack[0].Define(100,10,MAGIC,"& ^blast * with your staff");
	LoadedAttack[1].Define(100,10,WEAPON,"& ^hit * with your scythe");
	LoadedAttack[2].Define(100,3,NORMAL,"& ^bites *"); //for the rat.
	LoadedAttack[3].Define(100,10,NORMAL,"& ^hits *");
	LoadedAttack[4].Define(100,10,WEAPON,"& ^hits *");
	LoadedAttack[5].Define(200,10,NORMAL,"& lunges forward and ^hits *");
	LoadedAttack[6].Define(100,15,NORMAL,"& ^bites *");
	LoadedAttack[7].Define(100,10,NORMAL,"& ^claws *");
	LoadedAttack[8].Define(100,10,MAGIC,"& ^blasts * with a staff");
	LoadedAttack[9].Define(100,10,WEAPON,"& ^hits * with a scythe");
}
void GameInfo::InitActors(){
	LoadedActor[0].Define(0,"rat",'r',B_BLACK,STANDARD,20,90);
	LoadedActor[0].AddAttack(2);
	LoadedActor[1].Define(1,"goblin",'g',B_GREEN,STANDARD,50,100);
	LoadedActor[1].AddAttack(4);
	LoadedActor[2].Define(2,"skeleton",'s',B_WHITE,STANDARD,40,95);
	LoadedActor[2].AddAttack(4);
	LoadedActor[2].GainAttr(UNDEAD); LoadedActor[2].GainAttr(NO_FLESH);
	LoadedActor[3].Define(3,"zombie",'z',B_BLACK,STANDARD,55,120);
	LoadedActor[3].AddAttack(3);
	LoadedActor[3].AddAttack(6);
	LoadedActor[3].GainAttr(UNDEAD);
	LoadedActor[4].Define(4,"ghoul",'g',GREEN,STANDARD,50,100);
	LoadedActor[4].AddAttack(7);
	LoadedActor[4].GainAttr(UNDEAD); LoadedActor[4].GainAttr(PARALYSIS_HIT);
	LoadedActor[5].Define(5,"mummy",'M',YELLOW,STANDARD,45,110);
	LoadedActor[5].AddAttack(3);
	LoadedActor[5].GainAttr(UNDEAD); LoadedActor[5].GainAttr(CURSE_HIT); LoadedActor[5].GainAttr(FIRE_WEAKNESS);
	//maybe the rotting curse deals a little damage if you recall?
	//	-makes so much more sense as a curse!
	LoadedActor[6].Define(6,"wight",'W',CYAN,STANDARD,50,90);
	LoadedActor[6].AddAttack(3);
	LoadedActor[6].GainAttr(UNDEAD); LoadedActor[6].GainAttr(LIFE_DRAIN_HIT);
	LoadedActor[7].Define(7,"shade",'S',B_BLUE,STANDARD,40,100);
	LoadedActor[7].AddAttack(3);
	LoadedActor[7].GainAttr(UNDEAD);
	//look at mummy, wight, etc. should any of these have lower damage for balance reasons?
	//	-should wight and/or wraith touch to attack?
	LoadedActor[8].Define(8,"spirit",'G',B_WHITE,STANDARD,30,90);
	LoadedActor[8].AddAttack(3);
	LoadedActor[8].GainAttr(UNDEAD); LoadedActor[8].GainAttr(INCORPOREAL);
	LoadedActor[9].Define(9,"wraith",'G',RED,STANDARD,40,100);
	LoadedActor[9].AddAttack(3);
	//should any guys get claw or bite instead of hit?
	LoadedActor[9].GainAttr(UNDEAD); LoadedActor[9].GainAttr(INCORPOREAL); LoadedActor[9].GainAttr(STRENGTH_DRAIN_HIT);
	LoadedActor[10].Define(10,"banshee",'G',B_YELLOW,STANDARD,35,95);
	LoadedActor[10].AddAttack(3);
	LoadedActor[10].GainAttr(UNDEAD); LoadedActor[10].GainAttr(INCORPOREAL);
	LoadedActor[11].Define(11,"spectre",'G',B_GREEN,STANDARD,50,100);
	LoadedActor[11].AddAttack(3);
	LoadedActor[11].GainAttr(UNDEAD); LoadedActor[11].GainAttr(INCORPOREAL);
	LoadedActor[12].Define(12,"draugr",'d',MAGENTA,STANDARD,50,100);
	LoadedActor[12].AddAttack(3);
	LoadedActor[12].GainAttr(UNDEAD); LoadedActor[12].GainAttr(RESIST_WEAPONS);
	LoadedActor[13].Define(13,"guard",'p',BLUE,GUARD,70,95);
	LoadedActor[13].AddAttack(3);
	LoadedActor[14].Define(14,"townsperson",'p',B_YELLOW,TOWNSFOLK,40,100);
	LoadedActor[15].Define(15,"shady-looking character",'p',MAGENTA,SHADY_CHARACTER,50,95);
	LoadedActor[16].Define(16,"librarian",'p',B_WHITE,LIBRARIAN,35,120);
	LoadedActor[17].Define(17,"trinket salesman",'p',RED,TRINKET_SALESMAN,60,100);
	LoadedActor[18].Define(18,"necromancer",'@',BLUE,NECRO,50,100);
}
void GameInfo::InitItems(){
	LoadedItem[0].Define(0,"talisman of speed",'"',B_WHITE,QUICK_MOVE,1); //i'm trying to randomize these colors anyway, right?
	LoadedItem[1].Define(1,"jagged talisman",'"',B_WHITE,EXTRA_DAMAGE,1);
	LoadedItem[2].Define(2,"talisman of toughness",'"',B_WHITE,MORE_HP,1);
	LoadedItem[3].Define(3,"talisman of banishment",'"',B_WHITE,CHANCE_OF_UNSUMMON,1);
	LoadedItem[4].Define(4,"mummy eye talisman",'"',B_WHITE,CURSE_IMMUNITY,1);
	LoadedItem[5].Define(5,"talisman of free action",'"',B_WHITE,FREE_ACTION,1);
	LoadedItem[6].Define(6,"sustaining talisman",'"',B_WHITE,SUSTAINED,1);
	LoadedItem[7].Define(7,"talisman of power",'"',B_WHITE,MANA_REGEN,1);
	LoadedItem[8].Define(8,"talisman of mana",'"',B_WHITE,EXTRA_MANA,1);
	LoadedItem[9].Define(9,"talisman of recall",'"',B_WHITE,FREE_RECALL,1);
	LoadedItem[10].Define(10,"fire talisman",'"',B_WHITE,T_RESIST_FIRE,1);
	LoadedItem[11].Define(11,"talisman of force",'"',B_WHITE,KNOCKBACK,1);
	LoadedItem[12].Define(12,"warrior's gear",'"',B_WHITE,W_GEAR,1);
	LoadedItem[13].Define(13,"elite's gear",'"',B_WHITE,E_GEAR,1);
	LoadedItem[14].Define(14,"archer's gear",'"',B_WHITE,A_GEAR,1);
	LoadedItem[15].Define(15,"shroud of flame",'"',B_WHITE,FLAME_SHROUD,1);
	LoadedItem[16].Define(16,"talisman of ghoulish speed",'"',B_WHITE,QUICKGHAST,1);
	LoadedItem[17].Define(17,"barrier talisman",'"',B_WHITE,BLOCK_SUMMONS,1);
	LoadedItem[18].Define(18,"talisman of aggression",'"',B_WHITE,AGGRO,1);
	LoadedItem[19].Define(19,"beeswax talisman",'"',B_WHITE,NO_SHEEP,1);
	LoadedItem[20].Define(20,"talisman of sacrifice",'"',B_WHITE,SACRIFICIAL,1);
	LoadedItem[21].Define(21,"staff",'|',RED,BLAST,1);
	LoadedItem[22].Define(22,"staff of fireballs",'|',RED,FIREBALL,1);
	LoadedItem[23].Define(23,"staff of dark energy",'|',RED,DARK_ENERGY,1);
	LoadedItem[24].Define(24,"staff of wind",'|',RED,WIND,1);
	LoadedItem[25].Define(25,"scythe",'|',RED,NO_EFFECT,1);
	LoadedItem[26].Define(26,"scythe of whirling",'|',RED,WHIRLING,1);
	LoadedItem[27].Define(27,"scythe of slaying",'|',RED,SLAYING,1);
	LoadedItem[28].Define(28,"soulclaimer scythe",'|',RED,EAT_GHOSTS,1);
	LoadedItem[29].Define(29,"corpse",'%',B_WHITE,NO_EFFECT,0);
	LoadedItem[30].Define(30,"the Moonscythe",'|',BLUE,NO_EFFECT,0);
}
void GameInfo::InitPlayer(){
	Player.Move(1,1); //voodoo
	Player.Define(-2,"you",'@',B_WHITE,HUMAN,50,100);
	Player.MakeAllied(true);
	Player.AddAttack(0);
	Player.AddAttack(1);
	for(int i=0;i<3;++i){
	Player.GainSpell(SpellType(i));
	}
	Player.GainGold(Roll(1,20)+20);
	Actor* temp = new Actor(LoadedActor[2],-1,-1);
	temp->MakeAllied(true);
	Player.AddMinion(temp);
	temp = new Actor(LoadedActor[3],-1,-1);
	temp->MakeAllied(true);
	Player.AddMinion(temp);
	temp = new Actor(LoadedActor[4],-1,-1);
	temp->MakeAllied(true);
	Player.AddMinion(temp);
}
void GameInfo::GenerateLevel(LevelType type){
	bool returning=false;
	if(type==old_level && currentlevel!=CAVE_3){
		returning=true;
	}
	old_level = currentlevel;
	town_alarm=false;
	Map.Clear();
	bool generated=false;
	while(!generated){
		generated = Map.Generate(type);
	}
	currentlevel=type;
	Player.RefreshLevel();
	int i = 1 + Roll(1,4);
	int j = Roll(2,3) + 4;
	if(!returning){
		for(;i>0;--i){
			CreateItem();
		}
	}
	if(type==CAVE_3){
		bool done=false;
		while(!done){
			int rr=rand()%ROWS;
			int rc=rand()%COLS;
			if(Map.TilePtr(rr,rc)->IsWalkable()){
				done=true;
				LoadedItem[30].CloneSelf(rr,rc);
				LoadedActor[18].CloneSelf(rr,rc);
			}
		}
	}
	for(;j>0;--j){
		SpawnMob(); //this will depend on level too - no townsfolk in the cave
	}
	if((type==FOREST_1 && old_level==TOWN) || (type==FOREST_2 && old_level==FOREST_1)){
		Player.Move(Player.Row(),COLS-2);
	}
	else{
		if((type==FOREST_1 && old_level==FOREST_2) || (type==TOWN && old_level==FOREST_1)){
			Player.Move(Player.Row(),1);
		}
		else{
			if((type==CAVE_1 && old_level==FOREST_2) || (type==CAVE_2 && old_level==CAVE_1) || (type==CAVE_3 && old_level==CAVE_2)){
				for(int i=0;i<ROWS;++i){
					for(int j=0;j<COLS;++j){
						if(Map.TilePtr(i,j)->ID()==7){
							Player.Move(i,j);
						}
					}
				}
			}
			else{
				if(type==TOWN && old_level==TOWN){
					bool done=false;
					while(!done){
						int rr=Roll(1,ROWS-2);
						int rc=Roll(1,COLS-2);
						if(Map.TilePtr(rr,rc)->IsWalkable() && Map.ActorPtr(rr,rc)==NULL){
							done=true;
							Player.Move(rr,rc);
						}
					}
				}
				else{
					for(int i=0;i<ROWS;++i){
						for(int j=0;j<COLS;++j){
							if(Map.TilePtr(i,j)->ID()==4){
								Player.Move(i,j);
							}
						}
					}
				}
			}
		}
	}
}
int GameInfo::ItemNum(){
	int i[NUM_ITEMS+12];
	int total=0;
	for(int k=0;k<NUM_ITEMS;++k){
		int j = LoadedItem[k].Rarity();
		if(j!=0 && rand()%j==0){
			i[total]=k;
			++total;
		}
	}
	if(total > 0){
		return i[rand()%total];
	}
	else{
		return ItemNum(); //yes, this could cause stack overflow, but it's unlikely. will fix eventually.
	}					//this is a candidate for a local function
}
int GameInfo::ActorNum(){
/*	int i[20];
	int total=0;
	for(int k=0;k<20;++k){
		int j = 1 + abs(LoadedActor[k].Level() - current_level);
		if(j<=5 && rand()%j==0){
			i[total]=k;
			++total;
		}
	}
	return i[rand()%total];*/
	return rand()%NUM_ACTORS;
}
void GameInfo::CreateItem(){
	bool done=false;
	for(int i=0;i<9999 && !done;++i){
		int rr = rand()%ROWS;
		int rc = rand()%COLS;
		if(Map.TilePtr(rr,rc)->IsWalkable()){
			done=true;
			LoadedItem[ItemNum()].CloneSelf(rr,rc);
		}
	}
}
void GameInfo::CreateItem(int r,int c){
	LoadedItem[ItemNum()].CloneSelf(r,c);
}
void GameInfo::CreateItem(Actor* ptr){
	LoadedItem[ItemNum()].CloneSelf(ptr->Row(),ptr->Col(),true);
}
void GameInfo::SpawnMob(){
	bool done=false;
	for(int i=0;i<9999 && !done;++i){
		int rr = rand()%ROWS;
		int rc = rand()%COLS;
		if(Map.TilePtr(rr,rc)->IsWalkable() && Map.ActorPtr(rr,rc)==NULL){
			done=true;
			LoadedActor[ActorNum()].CloneSelf(rr,rc);
		}
	}
}

